/**
 * 变量声明
 */
// 使用 var 声明的变量可以在包含它的函数、模块、命名空间或全局作用域内部任何位置被访问，包含它的代码块对此没有什么影响
// 有些人称此为 var 作用域 或 函数作用域，函数参数也使用函数作用域

// 这些作用域规则可能会引发一些错误，多次声明同一个变量并不会报错
function sumMatrix(matrix: number[][]) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i = 0; i < currentRow.length; i++) { // i 会被覆盖
            sum += currentRow[i];
        }
    }

    return sum;
}

// 捕获变量的怪异之处
for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

// 执行结果会返回 10 10 10 10 10 10 10 10 10 10

// 需要以下改造
// 一个通常的解决方法是使用立即执行的函数表达式（IIFE）来捕获每次迭代时i的值
for (var i = 0; i < 10; i++) {
    // capture the current state of 'i'
    // by invoking a function with its current value
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}
// 0 1 2 3 4 5 6 7 8 9

// 使用 let 关键字定义变量可以解决 var 存在的作用域问题
let str = 'Hello'

// 当使用 let 声明一个变量，它使用的是 词法作用域 或者 块作用域
// 块作用域在包含他们的块或者for循环之外是不能访问的
function f(input: boolean) {
    let a = 100;

    if (input) {
        // Still okay to reference 'a'
        let b = a + 1;
        return b;
    }

    // Error: 'b' doesn't exist here
    // b 没有定义
    // return b;
}

// 在 cache 语句中声明的变量同样也有作用域规则
try {
    throw "oh No"
} catch (e) {
    console.log(e)
}
// console.log(e) 错误 e 不可访问

// 拥有块级作用域的另一个特点是：他们不能在声明之前进行读和写
// 在同一作用域中，不能重复定义变量

// 解构
// 函数参数
function add([num1, num2]: [number, number]): number {
    return num1 + num2
}

// 对象解构
let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let {a, b}: {a: string, b: number} = o;

// 函数声明
type C = { a: string, b?: number }
function f1({ a, b }: C): void {
    // ...
}
